﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using PSE.Framework.Data.Oracle;
using System.Data;
using System.Data.Common;
using PSE.Framework.Configuration;
using System.Configuration;
using PSE.Framework.Cryptography;
using Microsoft.Practices.EnterpriseLibrary.Data;
using PSE.Framework.Data.Oracle.Configuration;

namespace PSE.Framework.Service.Base.ResourceAccess
{
    /// <summary>
    /// Classe base para as classes de ResourceAccess
    /// </summary>
    public abstract class BaseResourceAccess : IDisposable
    {
        /// <summary>
        /// Construtor padrão
        /// </summary>
        public BaseResourceAccess()
        {
            OracleConnectionStringSection configSection = ApplicationConfiguration.GetCurrent().GetSection<OracleConnectionStringSection>("oracleConnectionString");

            if (configSection != null)
            {
                _dataBase = new OracleDatabase(GetConnectionString(configSection));
            }
            else
            {
                throw new ConfigurationErrorsException("The configuration section 'oracleConnectionString' has an unexpected declaration");
            }
        }

        private OracleDatabase _dataBase;

        /// <summary>
        /// Objeto para acesso ao banco de dados
        /// </summary>
        protected OracleDatabase DataBase
        {
            get { return _dataBase; }
        }

        /// <summary>
        /// Delegate para um método que controi uma entidade
        /// </summary>
        /// <typeparam name="T">Type da entidade</typeparam>
        /// <param name="reader">IDataReader com os dados da entidade</param>
        /// <returns>Objeto com a entidade</returns>
        protected delegate T ConstructEntity<T>(IDataReader reader) where T : new();

        /// <summary>
        /// Executa um commando no bando de dados e retorna uma entidade
        /// </summary>
        /// <typeparam name="T">Type da entidade</typeparam>
        /// <param name="command">Commando a ser executado</param>
        /// <param name="constructEntity">Contrutor da entidade</param>
        /// <returns>Objeto com a entidade</returns>
        protected T FindOne<T>(DbCommand command, ConstructEntity<T> constructEntity) where T : new()
        {
            T entity = default(T);

            using (IDataReader reader = this.DataBase.ExecuteReader(command))
            {
                if (reader.Read())
                {
                    entity = constructEntity(reader);
                }
            }

            return entity;
        }

        /// <summary>
        /// Executa um commando no banco de dados e retorna uma lista de entidades
        /// </summary>
        /// <typeparam name="T">Type da entidade</typeparam>
        /// <param name="command">Commando a ser executado</param>
        /// <param name="constructEntity">Contrutor da entidade</param>
        /// <returns>Lista de entidades</returns>
        protected List<T> FindAll<T>(DbCommand command, ConstructEntity<T> constructEntity) where T : new()
        {
            T entity = default(T);
            List<T> entities = new List<T>();

            using (IDataReader reader = this.DataBase.ExecuteReader(command))
            {
                while (reader.Read())
                {
                    entity = constructEntity(reader);
                    entities.Add(entity);
                }
            }

            return entities;
        }

        #region IDisposable Members

        public virtual void Dispose()
        {
            this.DataBase.Dispose();
        }

        #endregion

        private string GetConnectionString(OracleConnectionStringSection configSection)
        {
            StringBuilder connection = new StringBuilder();
            connection.Append("Pooling=" + configSection.Pooling.ToString());

            if (configSection.MinPoolSize.HasValue)
                connection.Append(";Min Pool Size=" + configSection.MinPoolSize.ToString());

            if (configSection.MaxPoolSize.HasValue)
                connection.Append(";Max Pool Size=" + configSection.MaxPoolSize.ToString());

            if (configSection.ConnectionTimeout.HasValue)
                connection.Append(";Connection Timeout=" + configSection.ConnectionTimeout.ToString());

            if (configSection.ConnectionLifetime.HasValue)
                connection.Append(";Connection Lifetime=" + configSection.ConnectionLifetime.ToString());

            if (configSection.DecrementPoolSize.HasValue)
                connection.Append(";Decr Pool Size=" + configSection.DecrementPoolSize.ToString());

            if (configSection.IncrementPoolSize.HasValue)
                connection.Append(";Incr Pool Size=" + configSection.IncrementPoolSize.ToString());

            connection.Append(";Password=" + RijndaelCryptographer.DecryptString(configSection.Password));
            connection.Append(";User ID=" + configSection.UserId);
            connection.Append(";Data Source=" + configSection.DataSource);
            connection.Append(";Persist Security Info=" + configSection.PersistSecurityInfo.ToString());

            return connection.ToString();
        }
    }
}
